package lz.controls.managers
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.InteractiveObject;
	import flash.display.SimpleButton;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	import flash.ui.Keyboard;
	import flash.utils.*;

	import lz.controls.Button;
	import lz.controls.core.UIComponent;

	/**
	 *  FocusManager 类为由鼠标或键盘作为 Tab 键循环导航的一组组件管理焦点。
	 * 一般使用 Tab 键来导航 Tab 键循环，焦点以循环方式在 Tab 键循环中的组件之间移动，从具有焦点的第一个组件到最后一个，然后再回到第一个。Tab 键循环包含所有组件和容器中支持 Tab 的组件。 一个应用程序可以包含许多 Tab 键循环。
	 * 一个 FocusManager 实例负责一个 Tab 键循环，虽然主应用程序始终与至少一个 FocusManager 实例相关联，但是应用程序使用不同的 FocusManager 实例来管理它包含的每个 Tab 键循环。
	 * 如果应用程序包含弹出窗口，可以要求附加 FocusManager 实例，例如，分别包含一个或多个组件的 Tab 键循环的情况。
	 * 可由 FocusManager 实例管理的所有组件都必须实现 fl.managers.IFocusManagerComponent 接口。
	 * 由 Flash Player 管理焦点的对象不需要实现 IFocusManagerComponent 接口。
	 * FocusManager 类还管理默认按钮的实现方式。
	 * 当在表单上按下 Enter 键时，根据当时焦点所在的位置，默认按钮会调度一个 click 事件。
	 * 如果文本区域具有焦点或正在组件中（例如，在 ComboBox 或 NumericStepper 组件中）编辑某个值，则默认按钮不会调度 click 事件。
	 */
	public class FocusManager implements IFocusManager
	{

		private var _form:DisplayObjectContainer;

		private var focusableObjects:Dictionary;

		private var focusableCandidates:Array;

		private var activated:Boolean=false;

		private var defButton:Button;

		private var _defaultButton:Button;

		private var calculateCandidates:Boolean=true;

		private var lastFocus:InteractiveObject;

		private var _showFocusIndicator:Boolean=true;

		private var lastAction:String;

		private var _defaultButtonEnabled:Boolean=true;

		/**
		 *  Creates a new FocusManager instance.
		 */
		public function FocusManager(container:DisplayObjectContainer)
		{
			focusableObjects=new Dictionary(true);
			if (container != null)
			{
				_form=container;
				activate();
			}
		}

		/**
		 *  Listen for children being added and see if they are focus candidates.
		 */
		private function addedHandler(event:Event):void
		{
			var target:DisplayObject=DisplayObject(event.target);
			// if it is truly parented, add it, otherwise it will get added 
			// when the top of the tree gets parented
			if (target.stage)
			{
				addFocusables(DisplayObject(event.target));
			}
		}

		/**
		 *  @private
		 *  Listen for children being removed.
		 */
		private function removedHandler(event:Event):void
		{
			var i:int;
			var o:DisplayObject=DisplayObject(event.target);
			if (o is IFocusManagerComponent && focusableObjects[o] == true)
			{
				if (o == lastFocus)
				{
					IFocusManagerComponent(lastFocus).drawFocus(false);
					lastFocus=null;
				}
				o.removeEventListener(Event.TAB_ENABLED_CHANGE, tabEnabledChangeHandler, false);
				delete focusableObjects[o];
				calculateCandidates=true;
			}
			else if (o is InteractiveObject && focusableObjects[o] == true)
			{
				var io:InteractiveObject=o as InteractiveObject;
				if (io)
				{
					if (io == lastFocus)
					{
						lastFocus=null;
					}
					delete focusableObjects[io];
					calculateCandidates=true;
				}
				o.addEventListener(Event.TAB_ENABLED_CHANGE, tabEnabledChangeHandler, false, 0, true);
			}
			removeFocusables(o);
		}

		/**
		 *  @private
		 *  Do a tree walk and add all children you can find.
		 */
		private function addFocusables(o:DisplayObject, skipTopLevel:Boolean=false):void
		{
			if (!skipTopLevel)
			{
				if (o is IFocusManagerComponent)
				{
					var focusable:IFocusManagerComponent=IFocusManagerComponent(o);
					if (focusable.focusEnabled)
					{
						if (focusable.tabEnabled && isTabVisible(o))
						{
							focusableObjects[o]=true;
							calculateCandidates=true;
						}
						o.addEventListener(Event.TAB_ENABLED_CHANGE, tabEnabledChangeHandler, false, 0, true);
						o.addEventListener(Event.TAB_INDEX_CHANGE, tabIndexChangeHandler, false, 0, true);
					}
				}
				else if (o is InteractiveObject)
				{
					var io:InteractiveObject=o as InteractiveObject;
					if (io && io.tabEnabled && findFocusManagerComponent(io) == io)
					{
						focusableObjects[io]=true;
						calculateCandidates=true;
					}
					io.addEventListener(Event.TAB_ENABLED_CHANGE, tabEnabledChangeHandler, false, 0, true);
					io.addEventListener(Event.TAB_INDEX_CHANGE, tabIndexChangeHandler, false, 0, true);
				}
			}
			if (o is DisplayObjectContainer)
			{
				var doc:DisplayObjectContainer=DisplayObjectContainer(o);
				o.addEventListener(Event.TAB_CHILDREN_CHANGE, tabChildrenChangeHandler, false, 0, true);
				if (doc is Stage || doc.parent is Stage || doc.tabChildren)
				{
					var i:int;
					for (i=0; i < doc.numChildren; i++)
					{
						try
						{
							var child:DisplayObject=doc.getChildAt(i);
							if (child != null)
							{
								addFocusables(doc.getChildAt(i));
							}
						}
						catch (error:SecurityError)
						{
							// Ignore this child if we can't access it
						}
					}
				}
			}
		}

		/**
		 *  Removes the DisplayObject and all its children.
		 */
		private function removeFocusables(o:DisplayObject):void
		{
			if (o is DisplayObjectContainer)
			{
				o.removeEventListener(Event.TAB_CHILDREN_CHANGE, tabChildrenChangeHandler, false);
				o.removeEventListener(Event.TAB_INDEX_CHANGE, tabIndexChangeHandler, false);
				for (var p:Object in focusableObjects)
				{
					var dob:DisplayObject=DisplayObject(p);
					if (DisplayObjectContainer(o).contains(dob))
					{
						if (dob == lastFocus)
						{
							lastFocus=null;
						}
						dob.removeEventListener(Event.TAB_ENABLED_CHANGE, tabEnabledChangeHandler, false);
						delete focusableObjects[p];
						calculateCandidates=true;
					}
				}
			}
		}

		/**
		 *  @private
		 *  Checks if the DisplayObject is visible for the tab loop.
		 */
		private function isTabVisible(o:DisplayObject):Boolean
		{
			var p:DisplayObjectContainer=o.parent;
			while (p && !(p is Stage) && !(p.parent && p.parent is Stage))
			{
				if (!p.tabChildren)
				{
					return false;
				}
				p=p.parent;
			}
			return true;
		}

		/**
		 *  @private
		 *  Checks if the DisplayObject is a valid candidate for the tab loop.
		 */
		private function isValidFocusCandidate(o:DisplayObject, groupName:String):Boolean
		{
			if (!isEnabledAndVisible(o))
			{
				return false;
			}
			if (o is IFocusManagerGroup)
			{
				var tg:IFocusManagerGroup=IFocusManagerGroup(o);
				if (groupName == tg.groupName)
				{
					return false;
				}
			}
			return true;
		}

		/**
		 *  @private
		 *  Checks if the DisplayObject is enabled and visible, or
		 *  a selectable or input TextField
		 */
		private function isEnabledAndVisible(o:DisplayObject):Boolean
		{
			var formParent:DisplayObjectContainer=DisplayObject(form).parent;
			while (o != formParent)
			{
				if (o is UIComponent)
				{
					if (!UIComponent(o).enabled)
					{
						// The DO is a disabled UIComponent
						return false;
					}
				}
				else if (o is TextField)
				{
					var tf:TextField=TextField(o);
					if (tf.type == TextFieldType.DYNAMIC || !tf.selectable)
					{
						// The DO is a dynamic or non-selectable TextField
						return false;
					}
				}
				else if (o is SimpleButton)
				{
					var sb:SimpleButton=SimpleButton(o);
					if (!sb.enabled)
					{
						// The DO is a disabled SimpleButton
						return false;
					}
				}
				if (!o.visible)
				{
					// The DO is not visible
					return false;
				}
				o=o.parent;
			}
			return true;
		}

		/**
		 *  @private
		 *  Add or remove object if tabbing properties change.
		 */
		private function tabEnabledChangeHandler(event:Event):void
		{
			calculateCandidates=true;
			var o:InteractiveObject=InteractiveObject(event.target);
			var registeredFocusableObject:Boolean=(focusableObjects[o] == true);
			if (o.tabEnabled)
			{
				if (!registeredFocusableObject && isTabVisible(o))
				{
					if (!(o is IFocusManagerComponent))
					{
						o.focusRect=false;
					}
					focusableObjects[o]=true;

				}
			}
			else
			{
				if (registeredFocusableObject)
				{
					delete focusableObjects[o];
				}
			}
		}

		/**
		 *  @private
		 *  Called when a focusable object's <code>tabIndex</code> property changes.
		 */
		private function tabIndexChangeHandler(event:Event):void
		{
			calculateCandidates=true;
		}

		/**
		 *  @private
		 *  Add or remove object if tabbing properties change.
		 */
		private function tabChildrenChangeHandler(event:Event):void
		{
			if (event.target != event.currentTarget)
			{
				return;
			}
			calculateCandidates=true;
			var o:DisplayObjectContainer=DisplayObjectContainer(event.target);
			if (o.tabChildren)
			{
				addFocusables(o, true);
			}
			else
			{
				removeFocusables(o);
			}
		}

		/**
		 *  Activates the FocusManager instance.
		 */
		public function activate():void
		{
			if (activated)
			{
				return;
			}
			addFocusables(form);
			// Listen for focus changes, use weak references for the stage.
			form.addEventListener(Event.ADDED, addedHandler, false, 0, true);
			form.addEventListener(Event.REMOVED, removedHandler, false, 0, true);
			form.stage.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler, false, 0, true);
			form.stage.addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler, false, 0, true);
			form.addEventListener(FocusEvent.FOCUS_IN, focusInHandler, true, 0, true);
			form.addEventListener(FocusEvent.FOCUS_OUT, focusOutHandler, true, 0, true);
			form.stage.addEventListener(Event.ACTIVATE, activateHandler, false, 0, true);
			form.stage.addEventListener(Event.DEACTIVATE, deactivateHandler, false, 0, true);
			form.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, false, 0, true);
			form.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler, true, 0, true);
			activated=true;
			// Restore focus to the last component that had it if there was one.
			if (lastFocus)
			{
				setFocus(lastFocus);
			}
		}

		/**
		 *  Deactivates the FocusManager.
		 */
		public function deactivate():void
		{
			if (!activated)
			{
				return;
			}
			focusableObjects=new Dictionary(true);
			focusableCandidates=null;
			//lastFocus = null;
			//defButton = null;
			form.removeEventListener(Event.ADDED, addedHandler, false);
			form.removeEventListener(Event.REMOVED, removedHandler, false);
			form.stage.removeEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler, false);
			form.stage.removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler, false);
			form.removeEventListener(FocusEvent.FOCUS_IN, focusInHandler, true);
			form.removeEventListener(FocusEvent.FOCUS_OUT, focusOutHandler, true);
			form.stage.removeEventListener(Event.ACTIVATE, activateHandler, false);
			form.stage.removeEventListener(Event.DEACTIVATE, deactivateHandler, false);
			form.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, false);
			form.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler, true);
			activated=false;
		}


		/**
		 *  @private
		 */
		private function focusInHandler(event:FocusEvent):void
		{
			if (!activated)
				return;
			var target:InteractiveObject=InteractiveObject(event.target);
			if (form.contains(target))
			{
				lastFocus=findFocusManagerComponent(InteractiveObject(target));
					// handle default button here
				/*if (lastFocus is Button) {
					var x:Button = Button(lastFocus);
					// if we have marked some other button as a default button
					if (defButton) {
						// change it to be this button
						defButton.emphasized = false;
						defButton = x;
						x.emphasized = true;
					}
				} else {
					// restore the default button to be the original one
					if (defButton && defButton != _defaultButton) {
						defButton.emphasized = false;
						defButton = _defaultButton;
						_defaultButton.emphasized = true;
					}
				}*/
			}
		}

		/**
		 *  @private
		 *  Useful for debugging.
		 */
		private function focusOutHandler(event:FocusEvent):void
		{
			if (!activated)
				return;
			var target:InteractiveObject=event.target as InteractiveObject;
		}


		/**
		 *  @private
		 *  Restore focus to the element that had it last.
		 */
		private function activateHandler(event:Event):void
		{
			if (!activated)
				return;
			var target:InteractiveObject=InteractiveObject(event.target);
			if (lastFocus)
			{
				if (lastFocus is IFocusManagerComponent)
				{
					IFocusManagerComponent(lastFocus).setFocus();
				}
				else
				{
					form.stage.focus=lastFocus;
				}
			}
			lastAction="ACTIVATE";
		}

		/**
		 *  @private
		 *  Useful for debugging.
		 */
		private function deactivateHandler(event:Event):void
		{
			if (!activated)
				return;
			var target:InteractiveObject=InteractiveObject(event.target);
		}

		/**
		 *  @private
		 *  This gets called when mouse clicks on a focusable object.
		 *  We block Flash Player behavior.
		 */
		private function mouseFocusChangeHandler(event:FocusEvent):void
		{
			if (!activated)
				return;
			if (event.relatedObject is TextField)
			{
				return; // pass it on
			}
			event.preventDefault();
		}

		/**
		 *  @private
		 *  This function is called when the tab key is hit.
		 */
		private function keyFocusChangeHandler(event:FocusEvent):void
		{
			if (!activated)
				return;
			showFocusIndicator=true;
			if ((event.keyCode == Keyboard.TAB || event.keyCode == 0) && !event.isDefaultPrevented())
			{
				setFocusToNextObject(event);
				event.preventDefault();
			}
		}

		/**
		 *  @private
		 *  Watch for the Enter key.
		 */
		private function keyDownHandler(event:KeyboardEvent):void
		{
			if (!activated)
				return;
			if (event.keyCode == Keyboard.TAB)
			{
				lastAction="KEY";
				// this makes and orders the focusableCandidates array
				if (calculateCandidates)
				{
					sortFocusableObjects();
					calculateCandidates=false;
				}
			}
		/*if (defaultButtonEnabled
			&& event.keyCode == Keyboard.ENTER
			&& defaultButton && defButton.enabled)
		{
			sendDefaultButtonEvent();
		}*/
		}

		/**
		 *  @private
		 *  This gets called when the focus changes due to a mouse click.
		 */
		private function mouseDownHandler(event:MouseEvent):void
		{
			if (!activated)
				return;
			if (event.isDefaultPrevented())
			{
				return;
			}
			var o:InteractiveObject=getTopLevelFocusTarget(InteractiveObject(event.target));
			if (!o)
			{
				return;
			}
			showFocusIndicator=false;
			// Make sure the containing component gets notified.
			// As the note above says, we don't set focus to a TextField ever
			// because the player already did and took care of where
			// the insertion point is, and we also don't call setfocus
			// on a component that last the last object with focus unless
			// the last action was just to activate the player and didn't
			// involve tabbing or clicking on a component
			if ((o != lastFocus || lastAction == "ACTIVATE") && !(o is TextField))
			{
				setFocus(o);
			}
			lastAction="MOUSEDOWN";
		}


		/**
		 * Gets or sets the current default button.
		 */
		public function get defaultButton():Button
		{
			return _defaultButton;
		}

		/**
		 *  @private (setter)
		 */
		public function set defaultButton(value:Button):void
		{
			var button:Button=value ? Button(value) : null;
			if (button != _defaultButton)
			{
				if (_defaultButton)
				{
					_defaultButton.emphasized=false;
				}
				if (defButton)
				{
					defButton.emphasized=false;
				}
				_defaultButton=button;
				defButton=button;
				if (button)
				{
					button.emphasized=true;
				}
			}
		}

		/**
		 *  @private
		 *  Call this method to make the system
		 *  think the Enter key was pressed and the default button was clicked.
		 */
		public function sendDefaultButtonEvent():void
		{
			defButton.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
		}

		/**
		 *  @private
		 */
		private function setFocusToNextObject(event:FocusEvent):void
		{
			if (!hasFocusableObjects())
			{
				return;
			}
			var o:InteractiveObject=getNextFocusManagerComponent(event.shiftKey);
			if (o)
			{
				setFocus(o);
			}
		}

		/**
		 *  @private
		 */
		private function hasFocusableObjects():Boolean
		{
			for (var o:Object in focusableObjects)
			{
				return true;
			}
			return false;
		}

		/**
		 *  Retrieves the interactive object that would receive focus
		 *  if the user pressed the Tab key to navigate to the next object.
		 *  This method retrieves the object that currently has focus
		 *  if there are no other valid objects in the application.
		 *
		 */
		public function getNextFocusManagerComponent(backward:Boolean=false):InteractiveObject
		{
			if (!hasFocusableObjects())
			{
				return null;
			}
			if (calculateCandidates)
			{
				sortFocusableObjects();
				calculateCandidates=false;
			}
			// get the object that has the focus
			var o:DisplayObject=form.stage.focus;
			o=DisplayObject(findFocusManagerComponent(InteractiveObject(o)));
			var g:String="";
			if (o is IFocusManagerGroup)
			{
				var tg:IFocusManagerGroup=IFocusManagerGroup(o);
				g=tg.groupName;
			}
			var i:int=getIndexOfFocusedObject(o);
			var bSearchAll:Boolean=false;
			var start:int=i;
			if (i == -1)
			{
				// we didn't find it
				if (backward)
				{
					i=focusableCandidates.length;
				}
				bSearchAll=true;
			}
			var j:int=getIndexOfNextObject(i, backward, bSearchAll, g);
			return findFocusManagerComponent(focusableCandidates[j]);
		}

		/**
		 *  @private
		 */
		private function getIndexOfFocusedObject(o:DisplayObject):int
		{
			var n:int=focusableCandidates.length;
			var i:int=0;
			for (i=0; i < n; i++)
			{

				if (focusableCandidates[i] == o)
				{
					return i;
				}
			}
			return -1;
		}

		/**
		 *  @private
		 */
		private function getIndexOfNextObject(i:int, shiftKey:Boolean, bSearchAll:Boolean, groupName:String):int
		{
			var n:int=focusableCandidates.length;
			var start:int=i;
			while (true)
			{
				if (shiftKey)
				{
					i--;
				}
				else
				{
					i++;
				}
				if (bSearchAll)
				{
					if (shiftKey && i < 0)
					{
						break;
					}
					if (!shiftKey && i == n)
					{
						break;
					}
				}
				else
				{
					i=(i + n) % n;
					// came around and found the original
					if (start == i)
					{
						break;
					}
				}
				if (isValidFocusCandidate(focusableCandidates[i], groupName))
				{
					var o:DisplayObject=DisplayObject(findFocusManagerComponent(focusableCandidates[i]));
					if (o is IFocusManagerGroup)
					{
						// look around to see if there's a selected member in the tabgroup
						// otherwise use the first one we found.
						var tg1:IFocusManagerGroup=IFocusManagerGroup(o);
						for (var j:int=0; j < focusableCandidates.length; j++)
						{
							var obj:DisplayObject=focusableCandidates[j];
							if (obj is IFocusManagerGroup)
							{
								var tg2:IFocusManagerGroup=IFocusManagerGroup(obj);
								if (tg2.groupName == tg1.groupName && tg2.selected)
								{
									i=j;
									break;
								}
							}
						}
					}
					return i;
				}
			}
			return i;
		}


		/**
		 *  @private
		 */
		private function sortFocusableObjects():void
		{
			focusableCandidates=[];
			for (var o:Object in focusableObjects)
			{
				var c:InteractiveObject=InteractiveObject(o);
				if (c.tabIndex && !isNaN(Number(c.tabIndex)) && c.tabIndex > 0)
				{
					sortFocusableObjectsTabIndex();
					return;
				}
				focusableCandidates.push(c);
			}
			focusableCandidates.sort(sortByDepth);
		}

		/**
		 *  @private
		 */
		private function sortFocusableObjectsTabIndex():void
		{
			focusableCandidates=[];
			for (var o:Object in focusableObjects)
			{
				var c:InteractiveObject=InteractiveObject(o);
				if (c.tabIndex && !isNaN(Number(c.tabIndex)))
				{
					// if we get here, it is a candidate
					focusableCandidates.push(c);
				}
			}
			focusableCandidates.sort(sortByTabIndex);
		}

		/**
		 *  @private
		 */
		private function sortByDepth(aa:InteractiveObject, bb:InteractiveObject):Number
		{
			var val1:String="";
			var val2:String="";
			var index:int;
			var tmp:String;
			var tmp2:String;
			var zeros:String="0000";
			var a:DisplayObject=DisplayObject(aa);
			var b:DisplayObject=DisplayObject(bb);
			while (a != DisplayObject(form) && a.parent)
			{
				index=getChildIndex(a.parent, a);
				tmp=index.toString(16);
				if (tmp.length < 4)
				{
					tmp2=zeros.substring(0, 4 - tmp.length) + tmp;
				}
				val1=tmp2 + val1;
				a=a.parent;
			}
			while (b != DisplayObject(form) && b.parent)
			{
				index=getChildIndex(b.parent, b);
				tmp=index.toString(16);
				if (tmp.length < 4)
				{
					tmp2=zeros.substring(0, 4 - tmp.length) + tmp;
				}
				val2=tmp2 + val2;
				b=b.parent;
			}
			return val1 > val2 ? 1 : val1 < val2 ? -1 : 0;
		}

		/**
		 *  @private
		 */
		private function getChildIndex(parent:DisplayObjectContainer, child:DisplayObject):int
		{
			return parent.getChildIndex(child);
		}

		/**
		 *  @private
		 */
		private function sortByTabIndex(a:InteractiveObject, b:InteractiveObject):int
		{
			return (a.tabIndex > b.tabIndex ? 1 : a.tabIndex < b.tabIndex ? -1 : sortByDepth(a, b));
		}


		/**
		 *  @copy fl.managers.IFocusManager#defaultButtonEnabled
		 */
		public function get defaultButtonEnabled():Boolean
		{
			return _defaultButtonEnabled;
		}

		/**
		 *  @private
		 */
		public function set defaultButtonEnabled(value:Boolean):void
		{
			_defaultButtonEnabled=value;
		}

		/**
		 *  Gets the next unique tab index to use in this tab loop.
		 */
		public function get nextTabIndex():int
		{
			return 0;
		}

		/**
		 *  Gets or sets a value that indicates whether a component that has focus should be marked with a visual indicator of focus.
		 */
		public function get showFocusIndicator():Boolean
		{
			return _showFocusIndicator;
		}

		/**
		 *  @private
		 */
		public function set showFocusIndicator(value:Boolean):void
		{
			_showFocusIndicator=value;
		}

		/**
		 *  @private
		 */
		public function get form():DisplayObjectContainer
		{
			return _form;
		}

		/**
		 *  @private
		 */
		public function set form(value:DisplayObjectContainer):void
		{
			_form=value;
		}


		/**
		 *  Gets the interactive object that currently has focus.
		 *  Adobe recommends calling this method instead of using the Stage object
		 *  because this method indicates which component has focus.
		 *  The Stage might return a subcomponent in that component.
		 */
		public function getFocus():InteractiveObject
		{
			var o:InteractiveObject=form.stage.focus;
			return findFocusManagerComponent(o);
		}

		/**
		 *  Sets focus on an IFocusManagerComponent component. This method does
		 *  not check for component visibility, enabled state, or other conditions.
		 */
		public function setFocus(component:InteractiveObject):void
		{
			if (component is IFocusManagerComponent)
			{
				IFocusManagerComponent(component).setFocus();
			}
			else
			{
				form.stage.focus=component;
			}
		}

		/**
		 *  Sets the <code>showFocusIndicator</code> value to <code>true</code>
		 *  and draws the visual focus indicator on the object with focus, if any.
		 */
		public function showFocus():void
		{
		}

		/**
		 *  Sets the <code>showFocusIndicator</code> value to <code>false</code>
		 *  and removes the visual focus indicator from the object that has focus, if any.
		 */
		public function hideFocus():void
		{
		}

		/**
		 *  Retrieves the interactive object that contains the given object, if any.
		 *  The player can set focus to a subcomponent of a Flash component;
		 *  this method determines which interactive object has focus from
		 *  the component perspective.
		 */
		public function findFocusManagerComponent(component:InteractiveObject):InteractiveObject
		{
			var p:InteractiveObject=component;
			while (component)
			{
				if (component is IFocusManagerComponent && IFocusManagerComponent(component).focusEnabled)
				{
					return component;
				}
				component=component.parent;
			}
			// tab was set somewhere else
			return p;
		}

		/**
		 * @private
		 */
		private function getTopLevelFocusTarget(o:InteractiveObject):InteractiveObject
		{
			while (o != InteractiveObject(form))
			{
				if (o is IFocusManagerComponent && IFocusManagerComponent(o).focusEnabled && IFocusManagerComponent(o).mouseFocusEnabled && UIComponent(o).enabled)
				{
					return o;
				}
				o=o.parent;
				if (o == null)
				{
					break;
				}
			}
			return null;
		}
	}
}
